package cn.edu.gzgs.websocket;

import cn.edu.gzgs.dto.EmailListDTO;
import com.alibaba.fastjson.JSON;
import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.ConcurrentHashMap;

/**
 * WebSocket服务器
 */
@Slf4j
@Component
@ServerEndpoint("/websocket")
public class WebSocketServer {

    /**
     * 存放所有WebSocket连接的Map
     * key: 用户ID，value: WebSocket会话
     */
    private static final ConcurrentHashMap<String, Session> USER_SESSIONS = new ConcurrentHashMap<>();
    
    // 与前端保持一致的时间格式
    private static final DateTimeFormatter FRONTEND_TIME_FORMAT = DateTimeFormatter.ofPattern("yyyy年M月d日HH:mm:ss");

    /**
     * 连接建立时触发
     */
    @OnOpen
    public void onOpen(Session session) {
        log.info("WebSocket连接建立: {}", session.getId());
    }

    /**
     * 连接关闭时触发
     */
    @OnClose
    public void onClose(Session session) {
        // 从连接池中移除断开的连接
        String userId = getUserIdBySession(session);
        if (userId != null) {
            USER_SESSIONS.remove(userId);
            log.info("用户 {} 的WebSocket连接已关闭", userId);
        }
        log.info("WebSocket连接关闭: {}", session.getId());
    }

    /**
     * 收到消息时触发
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("收到WebSocket消息: {}", message);
        
        try {
            EmailListDTO msg = JSON.parseObject(message, EmailListDTO.class);
            handleMessage(msg, session);
        } catch (Exception e) {
            log.error("处理WebSocket消息时发生错误", e);
        }
    }

    /**
     * 连接发生错误时触发
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("WebSocket连接发生错误: {}", session.getId(), error);
    }

    /**
     * 处理接收到的消息
     */
    private void handleMessage(EmailListDTO message, Session session) {
        String type = message.getType();
        
        switch (type) {
            case "control_Update_WS_Id":
                handleUserConnection(message, session);
                break;
            case "general_Msg_Private":
                handlePrivateMessage(message);
                break;
            default:
                log.warn("未知的消息类型: {}", type);
        }
    }

    /**
     * 处理用户连接控制消息
     */
    private void handleUserConnection(EmailListDTO message, Session session) {
        String userId = message.getContent(); // 用户ID存放在content字段中
        
        if (userId == null || userId.trim().isEmpty()) {
            log.warn("用户ID为空，无法建立连接映射");
            return;
        }

        // 检查该用户是否已有连接
        Session existingSession = USER_SESSIONS.get(userId);
        if (existingSession != null && existingSession.isOpen()) {
            // 检查是否是同一个session，如果是同一个session则不需要处理
            if (existingSession.equals(session)) {
                log.info("用户 {} 的WebSocket连接重复注册，忽略", userId);
                return;
            }
            
            // 只有当前session不同时，才强制原有连接下线
            EmailListDTO offlineMsg = new EmailListDTO();
            offlineMsg.setType("control_user_offLine");
            offlineMsg.setContent("检测到您的账号在其他地方登录，您已被强制下线");
            
            sendMessageToSession(existingSession, offlineMsg);
            
            try {
                existingSession.close();
            } catch (IOException e) {
                log.error("关闭原有连接时发生错误", e);
            }
            
            log.info("用户 {} 的原有连接已被强制下线（异地登录）", userId);
        }

        // 建立新的连接映射
        USER_SESSIONS.put(userId, session);
        log.info("用户 {} 的WebSocket连接已建立", userId);
    }

    /**
     * 处理私信消息
     */
    private void handlePrivateMessage(EmailListDTO message) {
        String targetUserId = message.getAcceptId();
        Session targetSession = USER_SESSIONS.get(targetUserId);
        
        if (targetSession != null && targetSession.isOpen()) {
            sendMessageToSession(targetSession, message);
        } else {
            log.warn("目标用户 {} 不在线，无法发送私信", targetUserId);
        }
    }

    /**
     * 向指定用户发送消息
     */
    public static void sendMessageToUser(String userId, EmailListDTO message) {
        Session session = USER_SESSIONS.get(userId);
        if (session != null && session.isOpen()) {
            sendMessageToSession(session, message);
        } else {
            log.warn("用户 {} 不在线，无法发送消息", userId);
        }
    }

    /**
     * 向指定会话发送消息
     */
    private static void sendMessageToSession(Session session, EmailListDTO message) {
        try {
            String jsonMessage = JSON.toJSONString(message);
            session.getBasicRemote().sendText(jsonMessage);
        } catch (IOException e) {
            log.error("发送WebSocket消息失败", e);
        }
    }

    /**
     * 根据Session获取用户ID
     */
    private String getUserIdBySession(Session session) {
        for (String userId : USER_SESSIONS.keySet()) {
            if (USER_SESSIONS.get(userId).equals(session)) {
                return userId;
            }
        }
        return null;
    }

    /**
     * 发送项目申请被拒绝通知
     */
    public static void sendProjectRepulseNotification(String userId, String projectName) {
        EmailListDTO message = new EmailListDTO();
        message.setType("routerTo_Project_Repulse");
        message.setTitle("项目申请被拒绝");
        message.setContent("您的项目申请「" + projectName + "」已被拒绝，请查看详情");
        message.setAcceptId(userId);
        message.setTime(LocalDateTime.now().format(FRONTEND_TIME_FORMAT));
        message.setIsRead(false);
        
        sendMessageToUser(userId, message);
    }

    /**
     * 发送项目申请通过通知
     */
    public static void sendProjectPassNotification(String userId, String projectName) {
        EmailListDTO message = new EmailListDTO();
        message.setType("routerTo_Project_Pass");
        message.setTitle("项目申请通过");
        message.setContent("恭喜！您的项目申请「" + projectName + "」已通过审核");
        message.setAcceptId(userId);
        message.setTime(LocalDateTime.now().format(FRONTEND_TIME_FORMAT));
        message.setIsRead(false);
        
        sendMessageToUser(userId, message);
    }

    /**
     * 发送通知消息
     */
    public static void sendInformMessage(String userId, String title, String content) {
        EmailListDTO message = new EmailListDTO();
        message.setType("general_Msg_Inform");
        message.setTitle(title);
        message.setContent(content);
        message.setAcceptId(userId);
        message.setTime(LocalDateTime.now().format(FRONTEND_TIME_FORMAT));
        message.setIsRead(false);
        
        sendMessageToUser(userId, message);
    }

    /**
     * 发送系统消息
     */
    public static void sendSystemMessage(String userId, String title, String content) {
        EmailListDTO message = new EmailListDTO();
        message.setType("general_Msg_System");
        message.setTitle(title);
        message.setContent(content);
        message.setAcceptId(userId);
        message.setTime(LocalDateTime.now().format(FRONTEND_TIME_FORMAT));
        message.setIsRead(false);
        
        sendMessageToUser(userId, message);
    }

    /**
     * 强制用户下线
     */
    public static void forceUserOffline(String userId, String reason) {
        Session session = USER_SESSIONS.get(userId);
        if (session == null || !session.isOpen()) {
            log.info("用户 {} 当前没有活跃的WebSocket连接，无需强制下线", userId);
            return;
        }
        
        EmailListDTO message = new EmailListDTO();
        message.setType("control_user_offLine");
        message.setContent(reason != null ? reason : "您已被强制下线");
        message.setTime(LocalDateTime.now().format(FRONTEND_TIME_FORMAT));
        
        // 发送下线通知
        sendMessageToSession(session, message);
        log.info("已向用户 {} 发送强制下线通知: {}", userId, reason);
        
        // 关闭连接并移除映射
        try {
            session.close();
        } catch (IOException e) {
            log.error("强制关闭用户 {} 连接时发生错误", userId, e);
        }
        USER_SESSIONS.remove(userId);
        log.info("用户 {} 的WebSocket连接已被强制关闭并移除", userId);
    }
}